CSS - tutorial - 11 - @ rules (at-rules)

revision:


Content

CSS at-rules are very practical in terms of telling CSS how to behave. general rules or regular rules nesting rules or nested rules feature queries - @supports practical examples container queries enable to apply styles based on the size of the container


CSS at-rules are very practical in terms of telling CSS how to behave.

top

There are several of these rules like @media, @import, @font-face, and more.

The unique identifier is the @mark that comes before these rules. The at sign, '@' is followed by an identifier and includes everything up to the next semicolon ';' , or the next CSS block, whichever comes first.

These rules can be divided into two groups: general rules and nesting rules.


general rules or regular rules

top

General CSS at-rules need to be placed on top of the stylesheet, before all the other CSS attributes and properties, because they are defining the general settings of the CSS rules and will not be overwritten by other rules.

Syntax: @identifier (rule)

examples
@charset "utf-8";

@charset is the first CSS at-rule that needs to be declared in the stylesheet for defining character encoding and no other rules should precede it.

There are other ways of handling charset, like placing it on the HTTP header, but there are certain use cases for declaring it in CSS. For example, when we are using non-ASCII characters for the content property, the browser has different ways of figuring out the character encoding: setting it in the calling HTML file, or the server might set content type with a certain character encoding, in the response header.

So in most cases, this is managed, but in case the encoding of the calling or returned HTML is different from a certain stylesheet, then @charset needs to be declared in the CSS file.

examples

Example: @charset

          @charset "UTF-8";
          h1::before {
            content: "@charset ";
          }
          q {
            color: blue;
          }
          q::before {
            content: "∅  ";
          }
          q::after {
            content: "  ∅";
          }
      

@import is used to include CSS from another source in a current CSS file.

So when parsing the CSS file and encountering a @import rule, the browser makes an HTTP request to fetch the external stylesheet and include its CSS properties right where the @import rule is declared.

Since this rule was built to help developers include stylesheets from other sources, it is not possible to include it within any of the conditional group at-rules like @media, @page, and @document.

examples

Example: @import - wrong usage

        /* iPhone in Portrait and Landscape */
        @media only screen{
          @import 'custom.css';
        }
      

However, we can specify media-dependent @import rules, to avoid fetching resources for unsupported media types.

examples

Example: @import

 
      @import 'custom.css' screen and (orientation:landscape);
      

The @namespace rule was designed to help XML based namespaces that would prevent duplicate styles from interfering with each other.

Even though there are more sophisticated concepts like "SMACSS", there could be potential use cases for this rule in CSS.

The @namespace rule helps apply scoping for CSS that mix styles from different XML namespaces. Examples of XML namespaces are HTML, SVG, MathML, XLink, etc. This way, there will be no styling collisions between elements from different namespaces.


nesting rules or nested rules

top

These are a subset of CSS at-rules that store a subset of additional statements within them, some of which might be conditional to a specific situation. They usually follow up after the general rules.

Syntax: @identifier (rule)

@document is a unique rule that allows you to specify styles for a certain page, without affecting the styles of other pages.

This feature is deprecated and no longer recommended.

This page-based style customization comes in different forms:

You can specify the rules for a specific URL:

examples

Example: @document - url

        @document url(https://example.com/)
      

You can specify the rules for pages that their URLs start with:

examples

Example: @document - url

        @document  url-prefix(https://example.com/index)
      

You can specify the rules on a domain level and for all the pages related to it:

examples

example: @document - domain level

        @document domain(example.com)
      

You can also specify the rules for a predefined regex pattern you defined for more control:

examples

example: @document - url

        @document regexp("https:.*")
      

@font-face was introduced as one of the pathways toward using custom fonts on the web, bringing more stylish typographies to web pages.

@font-face is a nested rule, and with it, you get different properties for defining the font:

ascent-override : defines the ascent metric for the font. The ascent metric is the height above the baseline that CSS uses to lay out line boxes in an inline formatting context.

Syntax: {ascent-override: normal | percentage;}

descent-override : defines the descent metric for the font. The descent metric is the height below the baseline that CSS uses to lay out line boxes in an inline formatting context.

Syntax: {descent-override: normal | percentage;}

font-display : determines how a font face is displayed based on whether and when it is downloaded and ready to use.

Syntax: {font-display: auto | block | swap | fallback | optional;}

font-family : specifies a name that will be used as the font face value for font properties. You get access to an identifier name for your custom font when and if it is downloaded and available to be used.

Syntax: {font-family: "family-name";}

examples

Example: @font-face - font-family

        @font-face {
          font-family: "CustomFont";
        }
        // Possible usage
        p {
          font-family: 'CustomFont';
        }
      

font-stretch : a font-stretch value. Accepts two values to specify a range that is supported by a font-face, for example "font-stretch: 50% 200%;"

Syntax: {font-stretch: ultra-condensed | extra-condensed | condensed | semi-condensed | normal | semi-expanded | expanded | extra-expanded | ultra-expanded | percentage ;}

font-style : a font-style value. Accepts two values to specify a range that is supported by a font-face, for example "font-style: oblique 20deg 50deg;"

Syntax: {font-style: normal | italic | oblique | oblique with angle (30deg) | oblique with angle range (30deg 50deg);}

font-weight : a font-weight value. Accepts two values to specify a range that is supported by a font-face, for example "font-weight: 100 400;"

Syntax: {font-weight: normal | bold | number | multiple values;}

font-feature-settings : allows control over advanced typographic features in OpenType fonts.

font-variation-settings : allows low-level control over OpenType or TrueType font variations, by specifying the four letter axis names of the features to vary, along with their variation values.

Syntax: {font-variation-settings: normal | <string> <number>;}

line-gap-override : defines the line gap metric for the font.

Syntax: {line-gap-override: normal | percentage;}

size-adjust : defines a multiplier for glyph outlines and metrics associated with this font. This makes it easier to harmonize the designs of various fonts when rendered at the same font size.

Syntax: {size-adjust: percentage;}

src : specifies references to font resources including hints about the font format and technology. It is required for the @font-face rule to be valid. You define the source of the font data. The font data can come from an external source using url() or local one using local(). This way, if the font is not available locally in the site directory or user system, it will be downloaded from the external source. Additionally, you can pass a format parameter, to hint toward the format of the defined font.

Syntax: {src: url() | format() | tech() | local(<font-face-name>) | <font-face-name>;}

examples

Example: @font-face - src

        @font-face {
          font-family: 'Helvetica';
          src:  url('Helvetica') format('woff'),
                local('Helvetica.woff') format('woff');
        }
      

unicode-range : the range of Unicode code points to be used from the font.

@keyframes is a very handy rule for defining CSS animation rules.

With the CSS rules applied within @keyframes rule, we define CSS rules that need to be applied when the CSS animation name attached to the @keyframe rule is applied to an element.

Syntax:

      @keyframes slidein {
        from {
          transform: translateX(0%);
        }
      
        to {
          transform: translateX(100%);
        }
      }
    

Values:

custom-ident : a name identifying the keyframe list. This must match the identifier production in CSS syntax.

from : a starting offset of 0%.

to : an ending offset of 100%.

percentage : a percentage of the time through the animation sequence at which the specified keyframe should occur.

examples

Example: @keyframes

        // CSS
        @keyframes ANIMATION-NAME {
          0%   { opacity: 0; }
          100% { opacity: 1; }
        }
        // OR
        @keyframes ANIMATION-NAME {
          from { opacity: 0; }
          to { opacity: 1; }
        }
      

@media is one of the most common CSS at-rules, used for setting CSS styles that will be applied to elements at different screens and window sizes.

This is mainly used for responsive design, so developers can style elements at different window sizes that tend to resemble common mobile, tablet, and desktop devices.

Syntax:

        /* At the top level of your code */
        @media screen and (min-width: 900px) {
          article {
            padding: 1rem 3rem;
          }
        }
        
        /* Nested within another conditional at-rule */
        @supports (display: flex) {
          @media screen and (min-width: 900px) {
            article {
              display: flex;
            }
          }
        }
      

examples

Example: @media

        #box {
          background-color: green;
        }
        @media only screen and (max-width: 600px) {
          #box {
            background-color: yellow;
          }
        }
      

@supports is a conditional group rule that will apply its content if the browser meets the criteria of the given condition.

This rule tests whether a browser supports a feature, then applies the styles for those elements if the condition is met.

The @supports "CSS at-rule" let you specify declarations that depend on a browser's support for one or more specific CSS features. This is called a "feature query" and the rule may be placed at the top level of your code or nested inside any other "conditional group at-rule".
The supports condition consists of one or more "name-value pairs" combined by conjunctions (and), disjunctions (or), and/or negations (not). Precedence of operators can be defined with parentheses.

Syntax:

        @supports (<supports-condition>) {
          /* If the condition is true, use the CSS in this block. */
        }
        //combined conditions
        @supports (<<supports-condition>) and (<supports-condition>) {
          /* If both conditions are true, use the CSS in this block. */
        }

      

examples
          @supports (display: grid) {
              .site-content {
                  display: grid;
              }
          }
        
            @supports not (display: flex) {
                .el {
                    display: table;
                    /* ... */
                }
            }
                                
        
          @supports (display: grid) {
            .photo-layout {
                  display: grid;
                  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
                  grid-gap: 2rem;
            }
        }
      
        /* Check one supported condition */
        @supports (display: flex) {
          .module { display: flex; }
        }
        
        /* Check multiple conditions */
        @supports (display: flex) and (-webkit-appearance: checkbox) {
          .module { display: flex; }
        }
      

@page describes the aspect of layout changes that will be applied when printing the document.

It specifically contains pseudo-elements for styling the ":first page" as well as the ":left" and ":right" margins of the page.

Syntax:

        /* Targets all the pages */
        @page {
          size: 8.5in 9in;
          margin-top: 4in;
        }

        /* Targets all even-numbered pages */
        @page :left {
          margin-top: 4in;
        }

        /* Targets all odd-numbered pages */
        @page :right {
          size: 11in;
          margin-top: 4in;
        }

        /* Targets all selectors with `page: wide;` set */
        @page wide {
          size: a4 landscape;
        }
      

examples

Example: @page

 
        @page :first {
          margin: 1in;
        }
      

@counter-style defines specific counter styles that are not part of the predefined set of styles.

A @counter-style rule defines how to convert a counter value into a string representation.n (at candidate recommendation stage)

The initial version of CSS defined a set of useful counter styles (see: list-style-type property). However, although more styles were added to this set of predefined styles over the years, this system proved too restrictive to fulfill the needs of worldwide typography. The @counter-style at-rule addresses this shortcoming in an open-ended manner, by allowing authors to define their own counter styles when the pre-defined styles aren't fitting their needs.

examples

Example: @counter-style

        @counter-style thumbs {
          system: cyclic;
          symbols: "\1F44D";
          suffix: " ";
        }
        
        ul {
          list-style: thumbs;
        }
      

The @font-feature-values CSS at-rule let you use a common name in the font-variant-alternates property for features activated differently in OpenType.

The @font-feature-values at-rule may be used either at the top level of your CSS or inside any CSS conditional-group at-rule.

The @property CSS at-rule is part of the CSS Houdini umbrella of APIs.

It allows developers to explicitly define their CSS custom properties, allowing for property type checking, setting default values, and define whether a property can inherit values or not.

The @layer CSS at-rule is used to declare a cascade layer and can be used to define the order of precedence in case of multiple cascade layers.


feature queries - @supports

top

Feature queries are created using the CSS at-rule @supports, and are useful as they give web developers a way to test to see if a browser has support for a certain feature, and then provide CSS that will only run based on the result of that test.

CSS feature queries are part of the "CSS Conditional Rules module", which also contains the media query @media rule.

Feature queries behave in a similar way to media queries. The difference is that with a media query you are testing something about the environment, in which the web page is running. With feature queries you are testing browser support for CSS features.

Syntax: a feature query consists of the @supports rule, followed by the property name and value you would like to test for. You may not test for a bare property name such as display; the rule requires a property name and a value.

syntax:
        @supports (property: value) {
            CSS rules to apply
          }
    

The "@supports at-rule" associates a block of statements with a "supports condition".

The supports condition consists of one or more name-value pairs combined by conjunctions (and), disjunctions (or), and/or negations (not).
Precedence of operators can be defined with parentheses.

Declaration syntax: the most basic supports condition is a simple declaration (a property name followed by a value, separated by a colon). The declaration must be surrounded by parentheses.

examples

the following example returns true if the browser's "transform-origin" property considers 5% 5% valid:

              @supports (transform-origin: 5% 5%) {}
          

Function syntax: the second basic supports condition is a supports function, the syntax for these is supported by all browsers, but the functions themselves are still being standardized.

examples

Tests if the browser supports the tested selector syntax. The following example returns true if the browser supports the child combinator:

              @supports selector(A > B) {}
          

The "@supports CSS at-rule" lets you specify declarations that depend on a browser's support for one or more specific CSS features. This is called a feature query. The rule may be placed at the top level of your code or nested inside any other "conditional group at-rule".

examples

Example : @supports

          @supports (display: grid) {
              div {
                display: grid;
              }
          }
          

Example: @supports not

          @supports not (display: grid) {
              div {
                float: right;
              }
          }
      

In JavaScript, @supports can be accessed via the CSS object model interface "CSSSupportsRule".

The "not" operator can precede any expression to create a new expression, resulting in the negation of the original one.

examples

The following example returns true if the browser's transform-origin property doesn't consider 10em 10em 10em valid:

              @supports not (transform-origin: 10em 10em 10em) {}
          

As with any operator, the "not" operator can be applied to a declaration of any complexity.

examples

the following examples are both valid:

              @supports not (not (transform-origin: 2px)) {}
              @supports (display: grid) and (not (display: inline-grid)) {}
          

The "and" operator creates a new expression from the conjunction of two shorter expressions. It returns true only if both of the shorter expressions are also true.

examples

the following example returns true if and only if the two shorter expressions are simultaneously true:

              @supports (display: table-cell) and (display: list-item) {}
          

Multiple conjunctions can be juxtaposed without the need of more parentheses.

examples

the following are both equivalent:

              @supports (display: table-cell) and (display: list-item) and (display:contents) {}
              @supports (display: table-cell) and ((display: list-item) and (display:contents)) {}
          

The "or" operator creates a new expression from the disjunction of two shorter expressions. It returns true if one or both of the shorter expressions is also true.

examples

the following example returns true if at least one of the two shorter expressions is true:

              @supports (transform-style: preserve) or (-moz-transform-style: preserve) {}
          

Multiple disjunctions can be juxtaposed without the need of more parentheses.

examples

the following are both equivalent:

              @supports (transform-style: preserve) or (-moz-transform-style: preserve) or (-o-transform-style: preserve) or (-webkit-transform-style: preserve) {}
              @supports (transform-style: preserve-3d) or ((-moz-transform-style: preserve-3d) or ((-o-transform-style: preserve-3d) or (-webkit-transform-style: preserve-3d))) {}
          

Note: When using both "and" and "or" operators, the parentheses must be used to define the order in which they apply. Otherwise, the condition is invalid and the whole rule is ignored.


practical examples

top
example a
If your browser supports row-gap, the text and border will be red.
code:
              <div>
                  <div class="box" style="margin-left:5vw;">If your  browser supports row-gap, the text and border will be red.</div>
              </div>
              <style>
                  .box {border: 0.5vw solid blue; color: blue;}
                  @supports (row-gap: 1vw) {
                      .box {border: 0.5vw solid red;  color: red;}
                  }
              </style>
          
example b
If your browser does not support row-gap, the text and border will be red.
code:
                  <div>
                      <div class="box-b" style="margin-left:5vw;">If your browser does not support row-gap, the text and border 
                      will be red.</div>
                  </div>
                  <style>
                      .box-b {border: 0.5vw solid blue; color: blue;}
                      @supports not(row-gap: 1vw) {
                          .box-a {border: 0.5vw solid red;  color: red;}
                      }
                  </style>
              
example c
If your browser supports display: grid and shape-outside: circle(), the text and border will be red.
code:
                  <div>
                      <div class="box-c" style="margin-left:5vw;">If your browser supports display: grid and shape-outside: circle(), 
                      the text and border will be red.
                          </div>
                  </div>
                  <style>
                      .box-c {border: 0.5vw solid blue; color: blue;}
                      @supports (display: grid)  and (shape-outside:circle()){
                          .box-c {border: 0.5vw solid red;  color: red;}
                      }
                  </style>
              
example d
Box 1
Has more content
than the other boxes.
Box 2
Box 3
code:
                  <div class="wrapper">
                      <div class="box-1">Box 1<br>Has more content <br>than the other boxes.</div>
                      <div class="box-2">Box 2</div>
                      <div class="box-3">Box 3</div>
                  </div>
                  <style>
                      .box-1 {margin-left:5vw; float: left; width: 30%; border: .2vw solid rgb(95, 97, 110); 
                          border-radius: .5vw; padding: 1vw;}
                      @supports (display: grid) {
                          .wrapper {display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1vw;}
                          .box-1 {width: auto;}
                      }
                  </style>
              

container queries enable to apply styles based on the size of the container

top

Container queries allow you to query a parent element's size and style to determine the styles which should be applied to any of its children.

If, for example, a container has less space available in the surrounding context, you can hide certain elements or use smaller fonts.

Container queries are an alternative to media queries, which apply styles to elements based on viewport size or other device characteristics. Media queries can only access and leverage information from the viewport, which means they can only work on a macro view of a page layout.

Container queries are a more precise tool that can support any number of layouts or layouts within layouts.

Using container queries

To use container queries, you need to declare a containment context on an element so that the browser knows you might want to query the dimensions of this container later. To do this, use the container-type property with a value of "size", "inline-size", or "normal".

These values have the following effects:

size : the query will be based on the inline and block dimensions of the container. Applies layout, style, and size containment to the container.

inline-size : the query will be based on the inline dimensions of the container. Applies layout, style, and inline-size containment to the element.

normal : the element is not a query container for any container size queries, but remains a query container for container style queries.

examples
          <div class="post">
              <div class="card">
                  <h4>Card title</h4>
                  <p>Card content</p>
              </div>
          </div>
          <style>
              .post {container-type: inline-size;}
              /* Default heading styles for the card title */
              .card h4 {font-size: 1em; }
              /* If the container is larger than 700px */
              @container (min-width: 700px) {
                  .card h2 {font-size: 2em;}
              }
          </style>
      

Naming containment contexts

It's also possible to give a containment context a name using the "container-name" property. Once named, the name can be used in a "@container query" so as to target a specific container.

examples
            <div class="post">
                <div class="card">
                    <h4>Card title</h4>
                    <p>Card content</p>
                </div>
            </div>
            <style>
                .post {container-type: inline-size; container-name: sidebar;}
                @container sidebar (min-width: 700px) {
                .card {font-size: 2em;}
                }
            </style>
        

Shorthand container syntax

The shorthand way of declaring a containment context is to use the container property:

.post{ container: sidebar / inline-size;}

Container query length units

These specify a length relative to the dimensions of a query container. Components that use units of length relative to their container are more flexible to use in different containers without having to recalculate concrete length values.

The container query length units are:

cqw : 1% of a query container's width.
cqh : 1% of a query container's height.
cqi : 1% of a query container's inline size.
cqb: 1% of a query container's block size.
cqmin: the smaller value of either cqi or cqb.
cqmax: the larger value of either cqi or cqb.

Fallbacks for container queries

For browsers that don't yet support container queries, grid and flex can be used to create a similar effect for the card component used on this page.

Examples

examples

Shanghai trip!

June 1, 2023

There is a lot to say and write about this metropole, but words are arguably not enough to describe and depict the amazing facts in this city.

code:
                  <div class="card-container">
                      <div class="card-child">
                        <div class="image">
                          <img src="../pics/2018-Sh-04.jpg" alt="">
                        </div>
                        <div class="meta">
                          <h4>Shanghai trip!</h4>
                          <i>June 1, 2023</i>
                          <p>There is a lot to say and write about this metropole, but words are arguably not enough to 
                          describe and depict the amazing facts in this city.</p>
                        </div>
                      </div>
                  </div>
                  <style>
                      .card-container {container: card / inline-size;}
                      .card-child {display: grid; grid-template-columns: 1fr 1fr; }
                      .card-child h4 {font-size: clamp(1vw, 7cqi, 4vw);}
                      @container (max-width: 45vw) {
                          .card-child {grid-template-columns: 1fr;}
                          .meta {padding: 1vw;}
                          img {aspect-ratio: 16 / 9;}
                      }
          
                      @layer base {
                          .card-container {background: #ffe4e8; overflow: hidden; resize: horizontal; width: 25vw; max-width: 40vw; 
                              min-width: 15vw;}
                          .meta {padding: 2vw;}
                          img {width: 100%; height: 100%; aspect-ratio: 1/1; object-fit: cover; object-position: 20% 20%;}
                      }
                  </style>